##################################################################
# File : /lua/user/usercamera.lua
# Description : Provides direct control over a specific game camera. Also listens to request from the Sim and manipulates
# 				the cameras as instructed.
# Author : GPG
##################################################################

LastResetTimestamp = false

# We want the reset calls to happen quickly near each other; this makes sure that happens
# We are doing it this way because the keymapper doesn't support double tap natively
# so we have to manually script a double tap on a key.
function CamResetDoubleTap()
    local newTime = GetSystemTimeSeconds()
    if not LastResetTimestamp or newTime - LastResetTimestamp >= 0.25 then
        LastResetTimestamp = newTime
        return
    end

    LastResetTimestamp = newTime
    import("/lua/ui/game/zoomslider.lua").ToggleWideView()
end

function ProcessCameraRequests(reqs)
    for k,v in Sync.CameraRequests do
        if v.Exec then
            local cam = GetCamera(v.Name)
            if not cam then
                WARN('No cam called ', v.Name, ' - failed executing - ', v.Exec)
                continue
            end
            if v.Params then
                cam[v.Exec](cam, unpack(v.Params))
            else
                cam[v.Exec](cam)
            end
        elseif v.Type == 'CAMERA_MOVE' then
            ForkThread(WaitForCamera, v)
        elseif v.Type == 'CAMERA_TRACK_ENTITIES' then
            ForkThread(WaitForCamera,v)
        elseif v.Type == 'CAMERA_NOSE_CAM' then
            ForkThread(WaitForCamera,v)
        elseif v.Type == 'CAMERA_SET_ZOOM' then
            ForkThread(WaitForCamera,v)
        elseif v.Type == 'CAMERA_SYNC_PLAYABLE_RECT' then
            SyncPlayableRect(v.Region)
            local miniMapCam = GetCamera('MiniMap')
            if miniMapCam then
                miniMapCam:Reset()
            end
        elseif v.Type == 'CAMERA_SNAP' then
            local position = v.Marker.position
            local hpr = v.Marker.orientation
            local zoom = v.Marker.zoom
            GetCamera(v.Name):SnapTo(position, hpr, zoom)
        elseif v.Type == 'CAMERA_SPIN' then
            GetCamera(v.Name):Spin(v.HeadingRate, v.ZoomRate or 0)
        elseif v.Type == 'CAMERA_UNIT_SPIN' then
            local cam = GetCamera(v.Name)
            cam:SnapTo( v.Marker.position, v.Marker.orientation, v.Marker.zoom)
            cam:Spin(v.HeadingRate)
        elseif v.Type == 'CAMERA_MOVE_DEFAULT_HEADING' then
            ForkThread(WaitForCamera,v)
        elseif v.Type == 'CAMERA_SET_RESET_CLAMP' then
            ForkThread(WaitForCamera,v)
        elseif v.Type == 'CAMERA_SET_MAXIMUM_ZOOM' then
            ForkThread(WaitForCamera,v)
        elseif v.Type == 'CAMERA_SET_RESET_POSITION' then
            local cam = GetCamera(v.Name)
            if not cam then
                WARN('No cam called ', v.Name)
                continue
            end
            cam:SetResetPosition(v.Position)
            cam:SetMaximumZoom(v.Zoom)
        elseif v.Type == 'CAMERA_SET_CLAMP_RATIOS' then
            ForkThread(WaitForCamera, v)
        else
            error("Unknown Camera Request type specified.")
        end
    end
end

function WaitForCamera(req)
    local cam = GetCamera(req.Name)

    if req.Type == 'CAMERA_TRACK_ENTITIES' then
        cam:TrackEntities(req.Ents,req.Zoom,req.Time)
    elseif req.Type == 'CAMERA_SET_ZOOM' then
        cam:SetZoom(req.Zoom, req.Time)
    elseif req.Type == 'CAMERA_SET_CLAMP_RATIOS' then
        cam:SetClampRatios(req.Ratiox, req.Ratioy)    
    elseif req.Type == 'CAMERA_SET_MAXIMUM_ZOOM' then
        cam:SetMaximumZoom(req.Zoom)
    elseif req.Type == 'CAMERA_MOVE' then
        # Move to specified marker
        if req.Marker then
            local position = req.Marker.position
            local hpr = req.Marker.orientation
            local zoom = req.Marker.zoom
            cam:MoveTo(position,hpr,zoom,req.Time)
        # Move to specified region (make region visible)
        elseif req.Region then
            cam:MoveToRegion(req.Region,req.Time)
        else
            error("Invalid move request: " .. repr(req))
            SimCallback(req.Callback)
            return
        end
    elseif req.Type == 'CAMERA_MOVE_DEFAULT_HEADING' then
        cam:MoveToWithDefaultHeading( req.Marker.position, req.Marker.zoom, req.Time )
    else
        error("Invalid camera request: " .. repr(req))
        SimCallback(req.Callback)
        return
    end

    if req.Time > 0 then
        WaitFor(cam)
        SimCallback(req.Callback)
    end
end

function Init()
    SyncCallback:Add(
        function()
            ProcessCameraRequests(Sync.CameraRequests)
        end
    )
end
